/*
 * Viewer.cpp
 * Authors: 107 <107@mail.ru>
 * License: GPL >=2.0
 *
 * Canvas based viewer.
 * Provide ScorlledViewer class to place it on Form
 *
 */

#include "Viewer.h"
#include "macros.h"


// define UI stuff
#define	DEFAULT_SOFTKEY_HEIGHT		40
#define DEFAULT_BOTTOM_BORDER 		0
#define DEFAULT_PROGRESS_WIDTH 		5
#define	DEFAULT_PROGRESS_BG_COLOR	Color::COLOR_GREY
#define DEFAULT_PROGRESS_COLOR		Color::COLOR_BLACK
#define DEFAULT_TEXT_LEFT_SPACE 	0
#define DEFAULT_TEXT_RIGHT_SPACE 	2
#define DEFAULT_TEXT_COLOR			Color::COLOR_BLACK
#define DEFAULT_TEXT_BG_COLOR		Color::COLOR_WHITE
#define	DEFAULT_FONT_SIZE			20
#define SWAP_FILE					L"/Home/text.swp"
// TODO: config.
// move to other file ( config.h? ) and configure some from UI
#define BOTTOM_BORDER		DEFAULT_BOTTOM_BORDER
//#define PROGRESS_WIDTH 		DEFAULT_PROGRESS_WIDTH
#define	PROGRESS_BG_COLOR	DEFAULT_PROGRESS_BG_COLOR
#define PROGRESS_COLOR		DEFAULT_PROGRESS_COLOR
#define TEXT_LEFT_SPACE		DEFAULT_TEXT_LEFT_SPACE
#define TEXT_RIGHT_SPACE	DEFAULT_TEXT_RIGHT_SPACE
#define TEXT_TOP_SPACE		0
#define TEXT_COLOR			DEFAULT_TEXT_COLOR
#define TEXT_BG_COLOR		DEFAULT_TEXT_BG_COLOR
//#define FONT_SIZE			DEFAULT_FONT_SIZE
#define SOFTKEY_HEIGHT 		DEFAULT_SOFTKEY_HEIGHT


int	FONT_SIZE;
int PROGRESS_WIDTH;

using namespace Osp::Graphics;
using namespace Osp::Base;
using namespace Osp::Io;
using namespace Osp::Content;

EText::EText ( Dimension d ) : EnrichedText() {
	// TODO: load FONT_SIZE from config
	FONT_SIZE=0;
	PROGRESS_WIDTH=0;

	// calculate if not in config
	if ( ! FONT_SIZE ) {
		FONT_SIZE = d.height / 20;
	}

	if ( ! PROGRESS_WIDTH ) {
		PROGRESS_WIDTH = d.width / 30;
	}

	AppLog( "EText construct %d x %d, fsize=%d", d.width, d.height, FONT_SIZE );
	pTextElement = null;
	Construct( Dimension( d.width-PROGRESS_WIDTH-TEXT_LEFT_SPACE-TEXT_RIGHT_SPACE, d.height-BOTTOM_BORDER) );
	SetHorizontalAlignment(TEXT_ALIGNMENT_LEFT );
	SetVerticalAlignment(TEXT_ALIGNMENT_TOP );
	SetTextWrapStyle(TEXT_WRAP_CHARACTER_WRAP );
	SetTextAbbreviationEnabled(true);
}

EText::~EText ( void ) {
	AppLog( "EText destruct" );
	if ( pTextElement ) { delete pTextElement; };
}

void EText::SetText ( Osp::Base::String *ssBuff ) {
//	AppLog( "EText Set text %S", ssBuff->GetPointer() );
	if ( pTextElement ) { delete pTextElement; };
	pTextElement = new TextElement();
    pTextElement->Construct( *ssBuff );
    pTextElement->SetTextColor( TEXT_COLOR );
       {
           Font font;
           font.Construct(FONT_STYLE_PLAIN, FONT_SIZE);
           pTextElement->SetFont(font);
       }
    RemoveAllTextElements(true);
    Add(*pTextElement);
}

// Constructor
Viewer::Viewer( void  ) {
	pEText = null;
};


Viewer::~Viewer( void ) {
	if ( pEText ) { delete pEText; };
}

void Viewer::SetSize ( Osp::Graphics::Dimension dd ) {
	d = dd;
	if ( ! pEText ) {
		pEText = new EText ( d );
	} else {
	pEText->SetSize ( GetViewerDimension() );
	}
}

void Viewer::SetText ( Osp::Base::String *str ) {
	pEText->SetText( str );
}

Dimension Viewer::GetViewerDimension ( void ) {
	return Dimension(GetViewerBounds().width, GetViewerBounds().height);
}

Rectangle Viewer::GetViewerBounds ( void ) {
	return pEText?Rectangle( TEXT_LEFT_SPACE, TEXT_TOP_SPACE, d.width-PROGRESS_WIDTH-TEXT_LEFT_SPACE-TEXT_RIGHT_SPACE, d.height-BOTTOM_BORDER-TEXT_TOP_SPACE ):Rectangle(0,0,0,0);
}


// Render Buff to real Canvas (for smooth scroll)
void Viewer::DrawViewer( void ) {
	Canvas *pDrCanvas = GetCanvas();
//	AppLog( "Draw Viewer %d %d - %d %d ", GetViewerBounds().x, GetViewerBounds().y, GetViewerBounds().width, GetViewerBounds().height );
 	pDrCanvas->FillRectangle( TEXT_BG_COLOR, GetViewerBounds() );
	pDrCanvas->DrawText( GetViewerBounds().GetTopLeft(), *pEText );
//	pDrCanvas->Show();
//	delete pDrCanvas;
}

// --- vertical progress bar ---

VProgress::VProgress ( void ) {
	cur_p = prev_p = 0;
};

VProgress::~VProgress ( void ) {
};


Rectangle VProgress::GetProgressBounds () {
	Rectangle r;
	r.x = d.width-PROGRESS_WIDTH;
	r.y = 0;
	r.width = PROGRESS_WIDTH;
	r.height = d.height - SOFTKEY_HEIGHT;
	return r;
}

void VProgress::DrawProgress( bool force=false ) {
	int cur_h = 0;
	/* calculete current %
	GetMaxIndex - 100 %
	GetCurrentIndex - ?cur_p%	*/
	/* calculate pixels
	h - 100%
	?cur_h - cur_p%	*/

	if ( GetMaxIndex( force ) < 1 ) return;
	cur_p = ( (GetCurrentIndex( force ) * 100) / GetMaxIndex( force ) );

	if ( prev_p < 0 || cur_p <0 ) return;

	AppLog( "prev=%d cur=%d", prev_p, cur_p );

	if ( cur_p == prev_p && !force ) {
		return;
	};
	// Draw or force draw
	Canvas *pDrCanvas = GetCanvas();
	Rectangle r = GetProgressBounds();
	pDrCanvas->SetLineWidth( PROGRESS_WIDTH );

//	AppLog ( "prog is %d,%d %d,%d", r.GetTopLeft().x, r.GetTopLeft().y, r.GetBottomRight().x, r.GetBottomRight().y );
	// clear
	if ( cur_p < prev_p || force ) {
		pDrCanvas->SetForegroundColor( PROGRESS_BG_COLOR );
		pDrCanvas->DrawLine( Point( r.x+PROGRESS_WIDTH/2, r.y ), Point( r.x+PROGRESS_WIDTH/2, r.height ) );
	}
	prev_p = cur_p;
	cur_h = ( ( cur_p * r.height ) / 100  );

	AppLog( "progress cur_h=%d, cur_p=%d, %d %d", cur_h, cur_p, r.x, r.y );
	pDrCanvas->SetForegroundColor( PROGRESS_COLOR );
	pDrCanvas->DrawLine( Point( r.x+PROGRESS_WIDTH/2, r.y ), Point( r.x+PROGRESS_WIDTH/2, cur_h ) );
//	pDrCanvas->Show();
//	delete pDrCanvas;
}

void VProgress::OnProgressTouch( int y ) {
	int set_p, goto_i;
	/*	y - ?set_p%
	    h - 100%	*/
	set_p = ( y * 100 / GetProgressBounds().height );
	/*	GetMaxIndex - 100%
		GotoIndex? - set_p	*/
	goto_i = ( GetMaxIndex() * set_p / 100 );
	GotoIndex ( goto_i );

}

// -------------- scrolled viewer -----------------
ScrolledViewer::ScrolledViewer  ( void ) : Viewer(), VProgress() {
	AppLog( "SViewer construct" );
	position = _cache_current_index = _cache_max_index = 0;
//    sFullText = "Welcome to FreeEdit";
	sFullText = " ";
    pEFullText = null;
    pCanvas = null;
};


ScrolledViewer::~ScrolledViewer ( void ) {
	AppLog( "ScrollView destruct" );
	if ( pEFullText ) { delete pEFullText; };
	if ( pEText ) { delete pEText; };
	if ( pCanvas) { delete pCanvas; };
}


Osp::Graphics::Canvas *ScrolledViewer::GetCanvas ( void ) {
	return pCanvas;
}

void ScrolledViewer::SetSize ( Osp::Graphics::Dimension dd ) {
	d = dd;
	AppLog ( "set new size %d x %d", d.width, d.height );
	Viewer::SetSize ( d );
	VProgress::SetSize ( d );

	if ( pCanvas ) { delete pCanvas; };
		pCanvas = new Canvas();
		pCanvas->Construct( Rectangle(0,0, d.width, d.height) );
		pCanvas->Clear();
	 	pCanvas->FillRectangle( TEXT_BG_COLOR, Rectangle( 0, 0, d.width, d.height ) );

	if ( ! pEFullText ) {
		pEFullText = new EText ( d );
		pEFullText->SetText( &sFullText );
	} else {
	pEFullText->SetSize ( GetViewerDimension() );
	}


	// recache variables
	_cache_max_index = GetMaxIndex( true );
	_cache_current_index = GetCurrentIndex( true );
}

Rectangle ScrolledViewer::GetCanvasBounds ( void ) {
	return pCanvas?pCanvas->GetBounds():Rectangle(0,0,0,0);
}


String	*ScrolledViewer::GetTextP ( void ) {
	return &sFullText;
};

String  ScrolledViewer::GetCurrentBuff ( void ) {
	return (pEText->pTextElement->GetText().GetLength()>0)?pEText->pTextElement->GetText():" ";
}

int ScrolledViewer::GetCurrentPosition ( void ) {
	return position;
}

int ScrolledViewer::GetCurrentIndex( bool force_nocache=false ) {
#ifdef API_2_0
	// XXX: bug till 2.0 to force GetLineIndex.
	// TODO: use it on 2.0
	return force_nocache?pEFullText->GetLineIndex( position ):_cache_current_index;
#else
	/* in 1.1 we calculate this on %
	GetTotalLineCount - 100%
	CurrentIndex - prev_p
	*/
	return force_nocache?(cur_p * pEFullText->GetTotalLineCount() / 100):_cache_current_index;
#endif
};

int ScrolledViewer::GetMaxIndex ( bool force_nocache=false ) {
	return force_nocache?pEFullText->GetTotalLineCount():_cache_max_index;
};

// **
void ScrolledViewer::Clear ( void ) {
	AppLog( "Clear Called" );
	String *psFullText = GetTextP();
	psFullText->Clear();
	psFullText->Append( " " );
	sFileName = "";
	cur_p = position = _cache_current_index = _cache_max_index = 0;
	pEFullText->SetText ( psFullText );
	AppLog( "cleared" );
	RenderCurrentPosition( true );
}

// **
void ScrolledViewer::LoadFromFile ( const String fname ) {
	File finput;


	finput.Construct( fname, "r" );
	IF_ERROR_RETURN( S( "IDS_CANT_OPEN_FILE" ) );

	AppLog ("start load %S", fname.GetPointer() );
	/*String */sBuff="";
	String *psFullText = GetTextP();
	psFullText->Clear();

	while ( finput.Read( sBuff ) == E_SUCCESS ) {
		if ( psFullText->EnsureCapacity( psFullText->GetLength() + sBuff.GetLength() ) == E_SUCCESS ) {
			psFullText->Append( sBuff );
		} else {
			// TODO: error could't local big text
			psFullText->Clear ();
			psFullText->Append( S("IDS_FILE_TOO_BIG") );
			break;
		}
	}
	// empty file
	if ( psFullText->GetLength() == 0 ) {
		psFullText->Append( " " );
	}
	AppLog ( "text len %d", psFullText->GetLength() );
	position = 	_cache_current_index = 0;
	sFileName = fname;
	pEFullText->SetText ( psFullText );
	_cache_max_index = pEFullText->GetTotalLineCount();
	RenderCurrentPosition();
	AppLog ("End load");
}

// **
void ScrolledViewer::SaveToFile ( const Osp::Base::String & path ) {
	sFileName = path;
	AppLog( "saved to file %S", path.GetPointer() );
	File fout;
	fout.Construct( SWAP_FILE , "w" );
	IF_ERROR_RETURN( S( "IDS_CANT_OPEN_FILE" ) );
	String *psFullText = GetTextP();
	fout.Write( *psFullText );
	IF_ERROR_RETURN( S( "IDS_CANT_WRITE_FILE" ) );
	fout.Flush();

	ContentManager contentManager;
	OtherContentInfo txtContent, txtContent2;
	ContentId contentId;
	contentManager.Construct();

	// delete them first
	if ( File::IsFileExist( path ) ) {
		AppLog( "delete first" );
		txtContent.Construct( path );
		contentId = contentManager.CreateContent( txtContent );
		contentManager.DeleteContent( contentId );
	}



#ifdef API_2_0
	AppLog ( " 2.0 create content" );
	// XXX: TODO: make it on 2.0
	/* for 2.0 do this and delete all till end of function */
 	String source( SWAP_FILE ) ;
	contentManager.CreateContent( source, path, true );
	IF_WARNING( S( "IDS_CANT_CREATE_CONTENT" ) );
#else
	AppLog( "copy" );
	result r = ContentManagerUtil::CopyToMediaDirectory( SWAP_FILE , path );
	TEST_R(r);
	IF_ERROR_RETURN( S( "IDS_CANT_COPY_FILE" ) );

	txtContent2.Construct( path );
	contentId = contentManager.CreateContent( txtContent2 );
	IF_WARNING( S( "IDS_CANT_CREATE_CONTENT" ) );
#endif

	File::Remove( SWAP_FILE );
	RenderCurrentPosition();
}

// **
void ScrolledViewer::InsertText ( Osp::Base::String str ) {
	String *pFText = GetTextP();
	pFText->Remove( GetCurrentPosition(), GetCurrentBuff().GetLength() );
	pFText->Insert( str, GetCurrentPosition() );
	pEFullText->SetText( pFText );
	_cache_max_index = pEFullText->GetTotalLineCount();
	RenderCurrentPosition();
}


// **
void ScrolledViewer::RenderCurrentPosition ( bool force ) {
	// render substring on Viewer
	int len=MAX_BUFF_LEN;
	sBuff="";
	// if string shorten then buffer => trunk bufflen
	if ( position + len >= GetTextP()->GetLength() ) {
		len = GetTextP()->GetLength() - position;
	};
	AppLog( " len is %d, pos %d", len, position );
	if ( len <= 1 ) {
		sBuff=" ";
	} else {
	GetTextP()->SubString( position, len, sBuff ); // fill sBuff on Viewer
	}
	SetText( &sBuff );
	DrawViewer();
	DrawProgress( force );

	// call virtual
	Canvas *pRealCanvas = GetRealCanvasN();
	if ( !IsFailed(GetLastResult()) && pRealCanvas && pCanvas && (d.width>0) && (d.height>0) ) {
	pRealCanvas->Copy( Point(0,0), *pCanvas, Rectangle ( 0, 0, d.width, d.height ) );
	pRealCanvas->Show();
	delete pRealCanvas;
	}
}

// Main enter point to move.
void ScrolledViewer::GotoIndex ( const int num ) {
	AppLog ( " goto %d max %d" , num, GetMaxIndex() );
	if ( num < 0 || num >= GetMaxIndex()-1 ) {
			return ; };
	_cache_current_index = num;
	position = pEFullText->GetFirstTextIndex( num );
	RenderCurrentPosition();
}

// **
void ScrolledViewer::Scroll ( const int lines ) {
	GotoIndex( GetCurrentIndex()+lines );
}

